package com.ruoyi.merchant.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.ruoyi.merchant.domain.Location;
import com.ruoyi.merchant.domain.Merchant;
import com.ruoyi.merchant.domain.vo.RescueOrderVO;
import com.ruoyi.merchant.service.IMerchantService;
import com.ruoyi.merchant.utils.RedisConst;
import com.ruoyi.merchant.utils.RedisGeoUtils;
import com.ruoyi.merchant.utils.RescueOrderWebSocket;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.Metrics;
import org.springframework.stereotype.Service;
import com.ruoyi.merchant.mapper.RescueOrderMapper;
import com.ruoyi.merchant.domain.RescueOrder;
import com.ruoyi.merchant.service.IRescueOrderService;
import org.springframework.transaction.annotation.Transactional;

import static com.ruoyi.merchant.utils.RedisConst.MERCHANT_LOCATION_EXPIRE;
import static com.ruoyi.merchant.utils.RedisConst.ORDER_SEARCHCOUNTKEY_TTL;

/**
 * 救援任务Service业务层处理
 *
 * @author ruoyi
 * @date 2025-07-14
 */
@Service
public class RescueOrderServiceImpl implements IRescueOrderService 
{
    @Autowired
    private RescueOrderMapper rescueOrderMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisGeoUtils redisGeoUtils;

    @Autowired
    private IMerchantService merchantService;

    @Autowired
    private RescueOrderWebSocket rescueOrderWebSocket;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    // 救援半径（公里）
    private static final double RESCUE_RADIUS = 5.0;

    /**
     * 查询救援任务
     * 
     * @param roId 救援任务主键
     * @return 救援任务
     */
    @Override
    public RescueOrder selectRescueOrderByRoId(Long roId)
    {
        return rescueOrderMapper.selectRescueOrderByRoId(roId);
    }

    /**
     * 查询救援任务列表
     * 
     * @param rescueOrder 救援任务
     * @return 救援任务
     */
    @Override
    public List<RescueOrder> selectRescueOrderList(RescueOrder rescueOrder)
    {
        return rescueOrderMapper.selectRescueOrderList(rescueOrder);
    }

    /**
     * 发布救援任务
     *
     * @param rescueOrder 救援任务
     * @return 结果
     */
    @Override
//    @Transactional
    public int insertRescueOrder(RescueOrderVO rescueOrder)
    {
        System.out.println(rescueOrder.toString());
        Long userId = rescueOrder.getUser().getUserId();

        // 检查用户是否已有订单
        int userOrderCount = rescueOrderMapper.findOrderByUserId(userId);
        if (userOrderCount > 0) {
            return 0;
        }

        // 插入订单
        int i = rescueOrderMapper.insertRescueOrder(rescueOrder);

        Long roId = rescueOrder.getRoId();
        System.out.println("插入成功，订单ID为：" + roId);
        Location fromLocation = rescueOrder.getFrom();

        // 2.插入成功之后将订单中的起点信息存入redis
        // 修正：交换经纬度顺序（Point构造参数应为：经度, 纬度）
        redisGeoUtils.add(RedisConst.LOCATION_USR_FROM + roId,
                new Point(fromLocation.getLongitude(), fromLocation.getLatitude()),
                userId);
        System.out.println("已缓存用户起点信息：" + roId);

        // 3.查看redis缓存中是否有商户的位置信息
        // 4.无则通过RedisGeoUtils工具类缓存所有商户的位置信息
        // 5. 缓存商户位置（如果不存在或已过期）
        String merchantLocationKey = RedisConst.LOCATION_MERCHANTS;
        cacheMerchantLocations(merchantLocationKey);

        // 6.设置推送规则(5分钟内无响应自动扩大公里数再次推送)
        // 7.通过websocket推送请求给对应的商户
        // 在redis中存储请求任务查询次数，初始值为0
        findAndNotifyNearbyMerchants(roId, fromLocation, merchantLocationKey);
        return 1;
    }

    /**
     * 缓存商户位置信息
     */
    private void cacheMerchantLocations(String key) {
        System.out.println("缓存商户位置信息...");
        // 检查缓存是否存在且未过期
        Boolean hasKey = stringRedisTemplate.hasKey(key);
        if (hasKey != null && hasKey) {
            return;
        }

        // 获取所有可用状态的商户
        Merchant merchant = new Merchant();
        merchant.setStatus(1L);      // 启用状态
        merchant.setIsDeleted(0L);   // 未删除
        merchant.setAuditStatus(1L); // 审核通过
        List<Merchant> merchants = merchantService.selectMerchantList(merchant);
        System.out.println("商户数量：" + merchants.size());

        // 批量缓存商户位置
        Map<Object, Point> merchantPoints = new HashMap<>();
        for (Merchant mer : merchants) {
            merchantPoints.put(mer.getuId(), new Point(mer.getLongitude(), mer.getLatitude()));
        }
        System.out.println("批量缓存商户位置...");

        if (!merchantPoints.isEmpty()) {
            redisGeoUtils.batchAdd(key, merchantPoints);
            // 设置缓存过期时间
            stringRedisTemplate.expire(key, MERCHANT_LOCATION_EXPIRE, TimeUnit.MINUTES);
        }
    }

    @Override
    public void findAndNotifyNearbyMerchants(Long roId, Location fromLocation, String merchantLocationKey) {
        // 获取对应请求的搜索次数
        String searchCountKey = "SEARCHCOUNT:" + roId;
        Long searchCount = stringRedisTemplate.opsForValue().increment(searchCountKey, 1);
        //设置或重置过期时间
        stringRedisTemplate.expire(searchCountKey, ORDER_SEARCHCOUNTKEY_TTL, TimeUnit.MINUTES);
        Point center = new Point(fromLocation.getLongitude(), fromLocation.getLatitude());

        //使用set集合记录已经推送过的商户
        Set<Long> notifiedMerchants = new HashSet<>();

        // 查找半径内的商户（按距离升序排序）
        List<GeoResult<RedisGeoCommands.GeoLocation<Object>>> results = redisGeoUtils.getNearbyMembers(
                merchantLocationKey,
                center,
                RESCUE_RADIUS*searchCount,
                Metrics.KILOMETERS,
                 null// 不限制数量
        );

        if (results == null || results.isEmpty()) {
            // 没有找到附近商户扩大搜索半径再次查找
            findAndNotifyNearbyMerchants(roId, fromLocation, merchantLocationKey);
        }

        // 存储附近商户信息（商户ID -> 距离）
        Map<Long, Double> nearbyMerchants = new HashMap<>();

        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> result : results) {
            RedisGeoCommands.GeoLocation<Object> location = result.getContent();
            Long uId = Long.parseLong(location.getName().toString());
            Distance distance = result.getDistance();

            // 存储商户信息
            nearbyMerchants.put(uId, distance.getValue());

            // 7. 通过WebSocket推送救援请求
//            rescueOrderWebSocket.notifyMerchant(uId, roId, distance);
        }
        // 8. 通过RabbitMQ将查询需要的参数Long roId, Location fromLocation, String merchantLocationKey发送给延时队列
        Map<String, Object> params = new HashMap<>();
        params.put("roId", roId);
        params.put("fromLocation", fromLocation);
        params.put("merchantLocationKey", merchantLocationKey);
        rabbitTemplate.convertAndSend("ex11", "rescue.params", params);

        //缓存附近商户信息（用于后续订单分配）
        String nearbyKey = RedisConst.ORDER_NEARBY_MERCHANTS + roId;
        Map<String, String> merchantDistanceMap = new HashMap<>();
        nearbyMerchants.forEach((id, dist) -> merchantDistanceMap.put(id.toString(), dist.toString()));
        stringRedisTemplate.opsForHash().putAll(nearbyKey, merchantDistanceMap);
        stringRedisTemplate.expire(nearbyKey, 30, TimeUnit.MINUTES); // 30分钟过期
    }

    /**
     * 修改救援任务
     * 
     * @param rescueOrder 救援任务
     * @return 结果
     */
    @Override
    public int updateRescueOrder(RescueOrder rescueOrder)
    {
        return rescueOrderMapper.updateRescueOrder(rescueOrder);
    }

    /**
     * 批量删除救援任务
     * 
     * @param roIds 需要删除的救援任务主键
     * @return 结果
     */
    @Override
    public int deleteRescueOrderByRoIds(Long[] roIds)
    {
        return rescueOrderMapper.deleteRescueOrderByRoIds(roIds);
    }

    /**
     * 删除救援任务信息
     * 
     * @param roId 救援任务主键
     * @return 结果
     */
    @Override
    public int deleteRescueOrderByRoId(Long roId)
    {
        return rescueOrderMapper.deleteRescueOrderByRoId(roId);
    }
}
